#ifndef DRIVER_H_
#define DRIVER_H_

#include "wolfie.h"

typedef string NombreTitulo;
typedef unsigned int accionesTotales;
using namespace aed2;

class Driver
{
	public:

		Driver(const Conj<Cliente>& clientes);

		~Driver();

		// PRE: nombre() \notin Titulos()
		void AgregarTitulo(const NombreTitulo& nombre, Dinero cotizacion, Nat max_acciones);

		// PRE: nombre \in Titulos()
		void ActualizarCotizacion(const NombreTitulo& nombre, Nat cotizacion);

		// PRE: no PrometeComprar(cliente)
		void AgregarPromesaDeCompra(const Cliente& cliente, const NombreTitulo& titulo, Dinero limite, Nat cantidad);

		// PRE: no PrometeVender(cliente) y cantidad < AccionesPorCliente(cliente, titulo)
		void AgregarPromesaDeVenta(const Cliente& cliente, const NombreTitulo& titulo, Dinero limite, Nat cantidad);

		Nat CantidadDeClientes() const;
		// PRE: 0 <= i < CantidadDeClientes()
		Cliente IesimoCliente(Nat i) const;

		Nat CantidadDeTitulos() const;
		// PRE: 0 <= i < CantidadDeTitulos()
		NombreTitulo IesimoTitulo(Nat i) const;

		// PRE: nombre_titulo \in Titulos()
		Nat MaxAccionesDe(const NombreTitulo& nombre_titulo) const;

		// PRE: nombre_titulo \in Titulos()
		Dinero CotizacionDe(const NombreTitulo& nombre_titulo) const;

		// PRE: nombre_titulo \in Titulos()
		bool EnAlza(const NombreTitulo& nombre_titulo) const;

		// PRE: c \in Clientes()
		Nat AccionesTotalesDe(const Cliente& cliente) const;

		// PRE: c \in Clientes(), nombre_titulo \in Titulos()
		Nat AccionesPorCliente(const Cliente& cliente, const NombreTitulo& nombre_titulo) const;

		// PRE: nombre_titulo \in Titulos()
		Nat AccionesDisponibles(const NombreTitulo& nombre_titulo) const;

		// PRE: c \in Clientes()
		bool PrometeComprar(const Cliente& cliente, const NombreTitulo& titulo) const;
		// PRE: c \in Clientes(), PrometeComprar(c, titulo)
		const NombreTitulo& TituloAComprar(const Cliente& cliente, const NombreTitulo& titulo) const;
		// PRE: c \in Clientes(), PrometeComprar(c, titulo)
		Nat CantidadAComprar(const Cliente& cliente, const NombreTitulo& titulo) const;
		// PRE: c \in Clientes(), PrometeComprar(c, titulo)
		Dinero ValorEsperadoParaComprar(const Cliente& cliente, const NombreTitulo& titulo) const;

		// PRE: c \in Clientes()
		bool PrometeVender(const Cliente& cliente, const NombreTitulo& titulo) const;
		// PRE: c \in Clientes(), PrometeComprar(c, titulo)
		const NombreTitulo& TituloAVender(const Cliente& cliente, const NombreTitulo& titulo) const;
		// PRE: c \in Clientes(), PrometeVender(c, titulo)
		Nat CantidadAVender(const Cliente& cliente, const NombreTitulo& titulo) const;
		// PRE: c \in Clientes(), PrometeVender(c, titulo)
		Dinero ValorEsperadoParaVender(const Cliente& cliente, const NombreTitulo& titulo) const;

	private:

	/******************************************************************
	 * TODO: Va a ser necesario instanciar privadamente un TAD Wolfie *
	 * con el cuál interactuar. Además, pueden declarar todas las     *
	 * funciones auxiliares que les hagan falta.                      *
	 ******************************************************************/

        wolfie* w;

        // PRE: Titulo ya fue agregado
        Titulo DameTitulo(const NombreTitulo& nombre_titulo) const;
        // PRE: Cliente existe en wolfie && tipoP == true => PrometeComprar(cliente, titulo) == true && tipoP == false => PrometeVender(cliente, titulo) == true
        Promesa DamePromesa(const Cliente& cliente, const NombreTitulo& titulo,const bool tipoP) const;

}; // class Driver


Driver::Driver(const Conj<Cliente>& clientes)
{
	//TODO
	w = new wolfie(clientes);

}

Driver::~Driver()
{
	//TODO

	if (w != NULL) {delete w;}

}

void Driver::AgregarTitulo(const NombreTitulo& nombre, Dinero cotizacion, Nat max_acciones)
{
	//TODO

    Titulo auxTitulo(nombre,cotizacion,max_acciones);

    w->agregarTitulo(auxTitulo);


}

void Driver::ActualizarCotizacion(const NombreTitulo& nombre, Nat cotizacion)
{
	//TODO
	w->actualizarCotizacion(nombre,cotizacion);

}

void Driver::AgregarPromesaDeCompra(const Cliente& cliente, const NombreTitulo& titulo, Dinero limite, Nat cantidad)
{
	//TODO
	Promesa auxPromesa(titulo,true,limite,cantidad);

	w->agregarPromesa(cliente,auxPromesa);

}

void Driver::AgregarPromesaDeVenta(const Cliente& cliente, const NombreTitulo& titulo, Dinero limite, Nat cantidad)
{
	//TODO
    Promesa auxPromesa(titulo,false,limite,cantidad);

	w->agregarPromesa(cliente,auxPromesa);

}

Nat Driver::CantidadDeClientes() const
{
	//TODO

	Conj<Cliente>::const_Iterador itConj = w->clientes();

    Nat n = 0;
    while (itConj.HaySiguiente()) {

        n++;
        itConj.Avanzar();

    }

    return n;

}

Cliente Driver::IesimoCliente(Nat i) const
{
	//TODO

//	titulos

	Conj<Cliente>::const_Iterador itConj = w->clientes();

    Nat n = 0;
    while (itConj.HaySiguiente()) {

        if(i == n) {return itConj.Siguiente();};
        n++;
        itConj.Avanzar();

    }

}

Nat Driver::CantidadDeTitulos() const
{
	//TODO

    Conj<Titulo>::const_Iterador itConj = w->titulos();

    Nat n = 0;
    while (itConj.HaySiguiente()) {

        n++;
        itConj.Avanzar();

    }

    return n;

}

NombreTitulo Driver::IesimoTitulo(Nat i) const
{
	//TODO

    Conj<Titulo>::const_Iterador itConj = w->titulos();

    Nat n = 0;
    while (itConj.HaySiguiente()){
	
        if(i == n){
		Titulo t = itConj.Siguiente();
		return t.nombre();
	}
        n++;
        itConj.Avanzar();
    }

}

Titulo Driver::DameTitulo(const NombreTitulo& nombre_titulo) const {

    Conj<Titulo>::const_Iterador itConj = w->titulos();

    while (itConj.HaySiguiente()) {
	Titulo t = itConj.Siguiente();
        if(t.nombre() == nombre_titulo){
			return itConj.Siguiente();
		}
		itConj.Avanzar();
    }
}

Nat Driver::MaxAccionesDe(const NombreTitulo& nombre_titulo) const
{
	//TODO
    return DameTitulo(nombre_titulo).maxAcciones();

}

Dinero Driver::CotizacionDe(const NombreTitulo& nombre_titulo) const
{
	//TODO

    return DameTitulo(nombre_titulo).cotizacion();

}

bool Driver::EnAlza(const NombreTitulo& nombre_titulo) const
{
	//TODO
    return DameTitulo(nombre_titulo).enAlza();

}

Nat Driver::AccionesTotalesDe(const Cliente& cliente) const
{
	Conj<Titulo>::const_Iterador it= w->titulos();
    Nat res=0;
	while(it.HaySiguiente()){
		res = res + w->accionesPorClientes(cliente, it.Siguiente().nombre());
		it.Avanzar();
	}
	return res;
}

Nat Driver::AccionesPorCliente(const Cliente& cliente, const NombreTitulo& nombre_titulo) const
{
	//TODO
	return w->accionesPorClientes(cliente,nombre_titulo);

}

Nat Driver::AccionesDisponibles(const NombreTitulo& nombre_titulo) const
{
	Nat max=MaxAccionesDe(nombre_titulo);
	Conj<Cliente>::const_Iterador it = w->clientes();
	Nat res=0;
	while(it.HaySiguiente()){
		res = res + w->accionesPorClientes(it.Siguiente(), nombre_titulo);
		it.Avanzar();
	}
	res=max-res;
	return res;
}

Promesa Driver::DamePromesa(const Cliente& cliente, const NombreTitulo& titulo, const bool tipoP) const {

    Conj<Promesa>::const_Iterador itConj = w->promesasDe(cliente);

	while (itConj.HaySiguiente()) {
		Promesa aux = itConj.Siguiente();
	        if (aux.titulo() == titulo && aux.tipo() == tipoP ){
			return itConj.Siguiente();
		}
		itConj.Avanzar();
	}

}

bool Driver::PrometeComprar(const Cliente& cliente, const NombreTitulo& titulo) const
{
	//TODO

	Conj<Promesa>::const_Iterador itConj = w->promesasDe(cliente);

	while (itConj.HaySiguiente()) {
		Promesa aux = itConj.Siguiente();
	        if (aux.titulo() == titulo && aux.tipo() == true ){
				return true;
			}
		itConj.Avanzar();
	}

    return false;

}

const NombreTitulo& Driver::TituloAComprar(const Cliente& cliente, const NombreTitulo& titulo) const
{
	return DamePromesa(cliente,titulo,true).titulo();

}

Nat Driver::CantidadAComprar(const Cliente& cliente, const NombreTitulo& titulo) const
{
	//TODO
	return DamePromesa(cliente,titulo,true).cantidad();

}

Dinero Driver::ValorEsperadoParaComprar(const Cliente& cliente, const NombreTitulo& titulo) const
{
	//TODO
	return DamePromesa(cliente,titulo,true).limite();


}

bool Driver::PrometeVender(const Cliente& cliente, const NombreTitulo& titulo) const
{
	//TODO
    Conj<Promesa>::const_Iterador itConj = w->promesasDe(cliente);

	while (itConj.HaySiguiente()) {
		Promesa aux = itConj.Siguiente();
		if (aux.titulo() == titulo && aux.tipo() == false){
			return true;
		}
		itConj.Avanzar();
	}

    return false;
}

const NombreTitulo& Driver::TituloAVender(const Cliente& cliente, const NombreTitulo& titulo) const
{
	//TODO
	return DamePromesa(cliente,titulo,false).titulo();

}

Nat Driver::CantidadAVender(const Cliente& cliente, const NombreTitulo& titulo) const
{
	//TODO
	return DamePromesa(cliente,titulo,false).cantidad();

}

Dinero Driver::ValorEsperadoParaVender(const Cliente& cliente, const NombreTitulo& titulo) const
{
	//TODO
	return DamePromesa(cliente,titulo,false).limite();

}
 // namespace aed2

#endif // DRIVER_H_
